package com.example.cloud.service.business.reservation.impl;

import com.example.cloud.constants.CommonConstants;
import com.example.cloud.constants.StateConstants;
import com.example.cloud.dao.business.reservation.ReservationRepository;
import com.example.cloud.dao.business.reservation.TreatmentTaskRepository;
import com.example.cloud.model.business.reservation.TreatmentTask;
import com.example.cloud.model.dto.Result;
import com.example.cloud.model.system.User;
import com.example.cloud.service.business.reservation.ReservationService;
import com.example.cloud.service.business.reservation.TreatmentTaskService;
import com.example.cloud.utils.BusinessUtil;
import com.example.cloud.utils.RequestUtil;
import org.beetl.sql.core.SQLManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by wangtao on 2018/5/17.
 */
@Service
public class TreatmentTaskServiceImpl implements TreatmentTaskService {

    private Logger logger = LoggerFactory.getLogger(TreatmentTaskServiceImpl.class);

    private final ReentrantLock lock = new ReentrantLock();// TODO jvm级别锁,架构是分布式，但目前只有一个服务

    @PersistenceContext
    EntityManager em;

    @Autowired
    private SQLManager sqlManager;

    @Autowired
    private TreatmentTaskRepository treatmentTaskRepository;

    @Autowired
    private ReservationRepository reservationRepository;

    @Autowired
    private ReservationService reservationService;

    @Override
    public Result doCheck(TreatmentTask record) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        User loginUser = RequestUtil.getLoginUser();
        StringBuilder sql = new StringBuilder("select * from treatment_task");
        // 当前账户
        sql.append(" where account_id = :accountId");
        // 当前组织机构
        sql.append(" and organ_id in(:organIds)");
        sql.append(" and (start_time between :startTime and :endTime or end_time between :startTime and :endTime)");
        sql.append(" and delete_flag = 0");
        Query nq = em.createNativeQuery(sql.toString(), TreatmentTask.class);
        nq.setParameter("accountId", loginUser.getAccountId());
        nq.setParameter("organIds", loginUser.getOrgans());
        nq.setParameter("startTime", record.getStartTime());
        nq.setParameter("endTime", record.getEndTime());
        List<TreatmentTask> resultList = nq.getResultList();
        // 专家校验
        if(record.getExpertId() != null){
            long count = resultList.parallelStream().filter(task -> record.getExpertId().equals(task.getExpertId())).count();
            if(count > 0){
                result.setCode(CommonConstants.FAILURE);
                result.setMsg("专家已被预约");
                return result;
            }
        }
        // 医生校验
        if(record.getDoctorId() != null){
            long count = resultList.parallelStream().filter(task -> record.getDoctorId().equals(task.getDoctorId())).count();
            if(count > 0){
                result.setCode(CommonConstants.FAILURE);
                result.setMsg("医生已被预约");
                return result;
            }
        }
        // 护士校验
        if(record.getNurseId() != null){
            long count = resultList.parallelStream().filter(task -> record.getNurseId().equals(task.getNurseId())).count();
            if(count > 0){
                result.setCode(CommonConstants.FAILURE);
                result.setMsg("护士已被预约");
                return result;
            }
        }
        // 治疗设备
        if(record.getEquipmentId() != null){
            long count = resultList.parallelStream().filter(task -> record.getEquipmentId().equals(task.getEquipmentId())).count();
            if(count > 0){
                result.setCode(CommonConstants.FAILURE);
                result.setMsg("医疗设备已被预约");
                return result;
            }
        }
        return result;
    }

    @Override
    @Transactional
    public Result doSave(TreatmentTask record) {
        Result result = new Result();
        result.setCode(CommonConstants.SUCCESS);
        try {
            // 这里使用超时锁，5秒内没有获取到锁即退出
            lock.tryLock(5, TimeUnit.SECONDS);
            result = doCheck(record);
            if(CommonConstants.SUCCESS.equals(result.getCode())){
                // 成功即保存
//                record.setMedicalNumber(record.getMedicalNumber());
                record.setAccountId(RequestUtil.getLoginUser().getAccountId());
                record.setOrganId(RequestUtil.getLoginUser().getOrganId());
                BusinessUtil.appendRecord(record);
                treatmentTaskRepository.save(record);
                // 更新预约记录的排班标识
                String sql = "update business_reservation set is_schedule = 1 where id = :id";
                Query nq = em.createNativeQuery(sql);
                nq.setParameter("id", record.getReservationId());
                nq.executeUpdate();
            }
        } catch (InterruptedException e) {
            logger.error("获取锁超时退出", e);
            result.setCode(CommonConstants.FAILURE);
            result.setMsg("请求超时,请稍后再试");
        } catch (Exception e) {
            logger.error("系统异常", e);
            throw e;
        } finally {
            lock.unlock();
        }
        return result;
    }

    @Override
    @Transactional
    public void doCancel(List<Long> ids) {
        // 删除治疗任务
        treatmentTaskRepository.deleteByReservationIdIn(ids);
        // 更新预约记录为未排班
        StringBuilder sql = new StringBuilder("update business_reservation set is_schedule = 0 where id in(:ids)");
        Query q = em.createNativeQuery(sql.toString());
        q.setParameter("ids", ids);
        q.executeUpdate();
    }

    @Override
    @Transactional
    public void doAdmission(TreatmentTask record) {
        // 更新治疗数据状态为治疗中
        StringBuilder sql = new StringBuilder("update treatment_task set `status` = :status where id = :id");
        Query nq = em.createNativeQuery(sql.toString());
        nq.setParameter("status", StateConstants.TREATING);
        nq.setParameter("id", record.getId());
        nq.executeUpdate();
        // 更新预约记录状态为已接诊，该方法使用事务传播方式为[REQUIRED]
        reservationService.doAdmission(record.getReservationId());
    }

    @Override
    @Transactional
    public void doComplete(TreatmentTask record) {
        // 更新治疗任务状态为已完成
        StringBuilder sql = new StringBuilder("update treatment_task set `status` = :status where id = :id");
        Query nq = em.createNativeQuery(sql.toString());
        nq.setParameter("status", StateConstants.TREAT_COMPLETE);
        nq.setParameter("id", record.getId());
        nq.executeUpdate();
        // 更新预约表医生完成状态
        sql = new StringBuilder("update business_reservation set doctor_status = :status where id = :id");
        nq = em.createNativeQuery(sql.toString());
        nq.setParameter("status", StateConstants.TREAT_COMPLETE);
        nq.setParameter("id", record.getReservationId());
        nq.executeUpdate();
        /**
         * 更新预约状态，只有在以下状态全部完成时才能完成
         * 医生状态：完成
         * 护士状态：完成
         * 客服状态：完成
         */
        // 校验是否可以更新预约为完成
        sql = new StringBuilder("update business_reservation set status = :status where id = :id and doctor_status = :checkStatus and nurse_status = :checkStatus and customer_status = :checkStatus");
        nq = em.createNativeQuery(sql.toString());
        nq.setParameter("id", record.getReservationId());
        nq.setParameter("status", StateConstants.RESERVATION_STATUS_COMPLETE);
        nq.setParameter("checkStatus", StateConstants.TREAT_COMPLETE);
        int c = nq.executeUpdate();
        if(c == 1){
            // 完成后删除副本
            sql = new StringBuilder("delete from business_visit_record where reservation_id = :id");
            nq = em.createNativeQuery(sql.toString());
            nq.setParameter("id", record.getReservationId());
            nq.executeUpdate();
        }
    }
}
